#ifndef AVALON_WEBSERVICEADAPTER_HPP
#define AVALON_WEBSERVICEADAPTER_HPP
#include <boost/call_traits.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include "Avalon/Services/Service.hpp"
#include "Avalon/WebServices/WebServices.hpp"

namespace Avalon {
namespace WebServices {
namespace Details {
  struct NullType {};
  template<typename Token, typename T0 = NullType, typename T1 = NullType,
    typename T2 = NullType, typename T3 = NullType, typename T4 = NullType,
    typename T5 = NullType, typename T6 = NullType, typename T7 = NullType,
    typename T8 = NullType, typename T9 = NullType>
  struct GetSignalType {};

  #ifndef AVALON_WEB_SERVICE_SIGNAL_TYPE_CALL_ARGUMENTS
    #define AVALON_WEB_SERVICE_SIGNAL_TYPE_CALL_ARGUMENTS 10
  #endif

  #define AVALON_WEB_SERVICE_SIGNAL_TYPE_DEFINE_PARAMETER(z, n, q)             \
    BOOST_PP_COMMA_IF(n) typename boost::call_traits<T##n>::param_type

  #define BOOST_PP_LOCAL_MACRO(n)                                              \
  template<typename Token BOOST_PP_COMMA_IF(n)                                 \
    BOOST_PP_ENUM_PARAMS(n, typename T)>                                       \
  struct GetSignalType<Token, boost::tuple<                                    \
      BOOST_PP_ENUM_PARAMS(n, T) > > {                                         \
    typedef boost::signals2::signal<void (const Token& BOOST_PP_COMMA_IF(n)    \
      BOOST_PP_REPEAT(n, AVALON_WEB_SERVICE_SIGNAL_TYPE_DEFINE_PARAMETER, T))> \
      Type;                                                                    \
  };

  #define BOOST_PP_LOCAL_LIMITS                                                \
    (0, AVALON_WEB_SERVICE_SIGNAL_TYPE_CALL_ARGUMENTS)
  #include BOOST_PP_LOCAL_ITERATE()

  template<typename Signal, typename Token, typename T0 = NullType,
    typename T1 = NullType, typename T2 = NullType, typename T3 = NullType,
    typename T4 = NullType, typename T5 = NullType, typename T6 = NullType,
    typename T7 = NullType, typename T8 = NullType, typename T9 = NullType>
  struct EmitSignal {};

  #define AVALON_WEB_SERVICE_EMIT_SIGNAL_PASS_PARAMETER(z, n, q)               \
    BOOST_PP_COMMA_IF(n) boost::get<n>(parameters)

  #define BOOST_PP_LOCAL_MACRO(n)                                              \
  template<typename Signal, typename Token BOOST_PP_COMMA_IF(n)                \
    BOOST_PP_ENUM_PARAMS(n, typename T)>                                       \
  struct EmitSignal<Signal, Token, boost::tuple<                               \
      BOOST_PP_ENUM_PARAMS(n, T) > > {                                         \
    void operator()(Signal* signal, const Token& token,                        \
        const boost::tuple< BOOST_PP_ENUM_PARAMS(n, T) >& parameters) const {  \
      (*signal)(token BOOST_PP_COMMA_IF(n) BOOST_PP_REPEAT(n,                  \
        AVALON_WEB_SERVICE_EMIT_SIGNAL_PASS_PARAMETER, T));                    \
    }                                                                          \
  };

  #define BOOST_PP_LOCAL_LIMITS                                                \
    (0, AVALON_WEB_SERVICE_SIGNAL_TYPE_CALL_ARGUMENTS)
  #include BOOST_PP_LOCAL_ITERATE()
}

  /*! \class WebServiceAdapter
   *  \brief Adapts an existing Avalon Service as a web service.
   *  \author Kamal Mansouri <kranar@hotmail.com>
   */
  template<typename Service>
  class WebServiceAdapter {
    public:

      //! Specifies the service's parameters.
      typedef typename Service::Parameters Parameters;

      //! Specifies the service's return type.
      typedef typename Service::ReturnType ReturnType;

      /*! \class Request
       *  \brief Stores the request details of the web service.
       */
      class Request {
        public:

          //! Signals a web service request.
          typedef typename Details::GetSignalType<
            WebServiceRequestToken<Service>, Parameters>::Type Signal;

          //! Constructs an empty Request.
          Request() {}

          ~Request() {}

          //! Polymorphically emits the signal corresponding to this Request.
          /*!
            \param signal The signal to emit.
            \param session The session making the Request.
            \param shuttle The Serializer used to encode the service.
            \param request The HTTP request that triggered this Request.
            \param response The response to this request.
          */
          void EmitSignal(boost::signals2::signal_base* signal,
              HttpSession* session, Serialization::Serializer* shuttle,
              HttpServerRequest* request, HttpServerResponse* response) {
            Details::EmitSignal<Signal, WebServiceRequestToken<Service>,
              Parameters>()(static_cast<Signal*>(signal),
              WebServiceRequestToken<Service>(session, shuttle, request,
              response), m_parameters);
          }

        private:
          friend class Serialization::DataShuttle;
          Parameters m_parameters;

          void Shuttle(Serialization::DataShuttle* shuttle,
              unsigned int version) {
            shuttle->Shuttle("parameters", m_parameters);
          }
      };
  };

  /*! \class WebServiceAdapter
   *  \brief Specialization for service with no parameters.
   *  \author Kamal Mansouri <kranar@hotmail.com>
   */
  template<typename T, typename R>
  class WebServiceAdapter<Avalon::Services::Service<T, R> > {
    public:

      //! Specifies the service.
      typedef Avalon::Services::Service<T, R> Service;

      //! Specifies the service's parameters.
      typedef typename Service::Parameters Parameters;

      //! Specifies the service's return type.
      typedef typename Service::ReturnType ReturnType;

      /*! \class Request
       *  \brief Stores the request details of the web service.
       */
      class Request {
        public:

          //! Signals a web service request.
          typedef typename Details::GetSignalType<
            WebServiceRequestToken<Service>, Parameters>::Type Signal;

          //! Constructs an empty Request.
          Request() {}

          ~Request() {}

          //! Polymorphically emits the signal corresponding to this Request.
          /*!
            \param signal The signal to emit.
            \param session The session making the Request.
            \param shuttle The Serializer used to encode the service.
            \param request The HTTP request that triggered this Request.
            \param response The response to this request.
          */
          void EmitSignal(boost::signals2::signal_base* signal,
              HttpSession* session, Serialization::Serializer* shuttle,
              HttpServerRequest* request, HttpServerResponse* response) {
            Details::EmitSignal<Signal, WebServiceRequestToken<Service>,
              Parameters>()(static_cast<Signal*>(signal),
              WebServiceRequestToken<Service>(session, shuttle, request,
              response), boost::tuple<>());
          }

        private:
          friend class Serialization::DataShuttle;

          void Shuttle(Serialization::DataShuttle* shuttle,
              unsigned int version) {}
      };
  };
}
}

#endif // AVALON_WEBSERVICEADAPTER_HPP
